"""
Security validation and file handling for EndoSight-UC AI Inference Server V3.0
Implements deep file validation, size limits, and content verification
"""

import magic
import hashlib
import logging
from typing import Tuple
from fastapi import HTTPException, UploadFile, status
from .config import settings

logger = logging.getLogger(__name__)


async def validate_image_file(file: UploadFile) -> bytes:
    """
    [修复 H3] 深度文件校验
    1. 检查文件大小
    2. 检查MIME类型 (头部)
    3. 检查文件内容 (Magic Number)
    4. 验证文件完整性
    """

    if not file or not file.filename:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="未选择文件",
        )

    # 1. 异步读取文件头部用于预验证
    try:
        file_header = await file.read(2048)
        await file.seek(0)  # 重置文件指针

        if len(file_header) == 0:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="空文件",
            )

    except Exception as e:
        logger.error(f"文件读取失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="无法读取文件",
        )

    # 2. 检查声称的 MIME 类型
    if file.content_type not in settings.ALLOWED_MIME_TYPES:
        logger.warning(f"不支持的文件Content-Type: {file.content_type}")
        raise HTTPException(
            status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
            detail=f"文件 Content-Type 错误。允许: {settings.ALLOWED_MIME_TYPES}",
        )

    # 3. 检查文件内容 (Magic Number) - 深度验证
    try:
        file_mime = magic.from_buffer(file_header, mime=True)
        logger.info(f"文件 magic 检测: {file_mime}, 声明: {file.content_type}")

        if file_mime not in settings.ALLOWED_MIME_TYPES:
            logger.warning(f"文件内容类型不匹配: {file_mime}")
            raise HTTPException(
                status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
                detail=f"文件内容类型不支持。检测到: {file_mime}, 允许: {settings.ALLOWED_MIME_TYPES}",
            )

        # 额外验证：确保声明的MIME和实际MIME匹配
        if not file_mime.startswith(file.content_type.split('/')[0]):
            logger.warning(f"MIME类型不匹配: 声明={file.content_type}, 实际={file_mime}")
            raise HTTPException(
                status_code=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE,
                detail=f"文件MIME类型不匹配",
            )

    except magic.MagicException as e:
        logger.error(f"Magic number 检测失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="文件格式无法识别",
        )
    except Exception as e:
        logger.error(f"文件验证过程出错: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="文件验证过程失败",
        )

    # 4. 读取完整文件并检查大小
    try:
        contents = await file.read()
        file_size = len(contents)

        # 检查文件大小限制
        max_size_bytes = settings.MAX_FILE_SIZE_MB * 1024 * 1024
        if file_size > max_size_bytes:
            logger.warning(f"文件过大: {file_size} bytes > {max_size_bytes} bytes")
            raise HTTPException(
                status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                detail=f"文件大小超过 {settings.MAX_FILE_SIZE_MB}MB 限制。实际: {file_size / (1024*1024):.1f}MB",
            )

        # 检查文件最小大小（防止空文件或过小文件）
        if file_size < 1024:  # 1KB 最小大小
            logger.warning(f"文件过小: {file_size} bytes")
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="文件过小，可能已损坏",
            )

        logger.info(f"文件验证成功: {file.filename}, 大小: {file_size} bytes, 类型: {file_mime}")
        return contents

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"读取完整文件失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="文件读取失败",
        )


def calculate_file_hash(contents: bytes) -> str:
    """Calculate SHA-256 hash of file contents for integrity verification"""
    try:
        return hashlib.sha256(contents).hexdigest()
    except Exception as e:
        logger.error(f"计算文件哈希失败: {e}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail="文件完整性检查失败",
        )


def validate_image_integrity(contents: bytes, expected_mime: str) -> Tuple[bool, str]:
    """
    Additional image integrity validation
    Returns (is_valid, error_message)
    """
    try:
        # Basic image format validation using PIL
        from PIL import Image
        import io

        image = Image.open(io.BytesIO(contents))

        # Verify image can be loaded and processed
        image.verify()  # Verify doesn't load pixel data

        # Check image dimensions are reasonable
        image = Image.open(io.BytesIO(contents))  # Reopen after verify
        width, height = image.size

        if width < 100 or height < 100:
            return False, f"图像尺寸过小: {width}x{height}"

        if width > 8192 or height > 8192:
            return False, f"图像尺寸过大: {width}x{height}"

        # Check total pixels is reasonable (to prevent memory issues)
        total_pixels = width * height
        if total_pixels > 67108864:  # 8192x8192
            return False, f"图像像素数过多: {total_pixels:,}"

        logger.info(f"图像完整性验证通过: {width}x{height}, 格式: {image.format}")
        return True, ""

    except ImportError:
        logger.warning("PIL not available for image integrity check")
        return True, ""
    except Exception as e:
        logger.error(f"图像完整性验证失败: {e}")
        return False, f"图像格式无效或已损坏: {str(e)}"


def sanitize_filename(filename: str) -> str:
    """Sanitize filename to prevent path traversal attacks"""
    if not filename:
        return "unknown_image.jpg"

    # Remove path separators and dangerous characters
    import re
    filename = re.sub(r'[<>:"/\\|?*]', '_', filename)
    filename = re.sub(r'\.\.', '_', filename)
    filename = re.sub(r'^\.+', '_', filename.lstrip('.'))

    # Limit filename length
    if len(filename) > 255:
        name, ext = filename.rsplit('.', 1) if '.' in filename else (filename, '')
        filename = name[:255-len(ext)-1] + '.' + ext if ext else name[:255]

    return filename


class SecurityValidator:
    """Centralized security validation class"""

    @staticmethod
    async def validate_upload(file: UploadFile) -> Tuple[bytes, dict]:
        """
        Complete file validation pipeline
        Returns (file_contents, metadata)
        """
        # Step 1: Basic file validation
        contents = await validate_image_file(file)

        # Step 2: Calculate file hash
        file_hash = calculate_file_hash(contents)

        # Step 3: Image integrity validation
        is_valid, error_msg = validate_image_integrity(contents, file.content_type)
        if not is_valid:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail=error_msg,
            )

        # Step 4: Generate secure filename
        secure_filename = sanitize_filename(file.filename)

        metadata = {
            'original_filename': file.filename,
            'secure_filename': secure_filename,
            'content_type': file.content_type,
            'file_size': len(contents),
            'file_hash': file_hash,
            'validation_passed': True
        }

        return contents, metadata


# Export main validation function
__all__ = [
    'validate_image_file',
    'calculate_file_hash',
    'validate_image_integrity',
    'sanitize_filename',
    'SecurityValidator'
]